﻿using AppUpdateInstaller.Model;
using ConsoleHoster.Common.Utilities;
using ConsoleHoster.Common.Utilities.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using TestUtilities;

namespace AppUpdateInstaller.UnitTests.ModelTests
{
	[TestClass]
	public class InstallationStageBaseTests
	{
		private Mock<IProcessManager> mockProcessManager;
		private Mock<IFileSystemContentProvider> mockFSContentProvider;

		private ActionMock mockShutdownAction;

		[TestInitialize]
		public void InitializeTest()
		{
			this.mockProcessManager = new Mock<IProcessManager>();
			this.mockFSContentProvider = new Mock<IFileSystemContentProvider>();

			this.mockShutdownAction = new ActionMock();
		}

		[TestMethod]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Constructor_ThrowsOnNullProcessManager()
		{
			new MockInstallationStage(null, this.FSContentProvider, this.ShutdownAction);
		}

		[TestMethod]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Constructor_ThrowsOnNullFileSystemContentProvider()
		{
			new MockInstallationStage(this.ProcessManager, null, this.ShutdownAction);
		}

		[TestMethod]
		public void Constructor_SetsAllPropertiesAsPassedIn()
		{
			MockInstallationStage tmpStage = new MockInstallationStage(this.ProcessManager, this.FSContentProvider, this.ShutdownAction);

			Assert.AreEqual(this.ProcessManager, tmpStage.BaseProcessManager, "Unexpected ProcessManager property value");
			Assert.AreEqual(this.FSContentProvider, tmpStage.BaseFileSystemContentProvider, "Unexpected FileSystemContentProvider property value");
			Assert.AreEqual(this.ShutdownAction, tmpStage.BaseShutdownAction, "Unexpected ShutdownAction property value");
		}

		[TestMethod]
		public void NotifyProgressChange_FilresTheProgressChangeEventWithPassedInArgument()
		{
			const string tmpStatus = "some property name";
			InstallationStageProgressEventArgs tmpEA = new InstallationStageProgressEventArgs { Status = tmpStatus };

			MockInstallationStage tmpStage = new MockInstallationStage(this.ProcessManager, this.FSContentProvider, this.ShutdownAction);

			IInstallationStage tmpCalledByStage = null;
			InstallationStageProgressEventArgs tmpCalledWithEventArgs = null;
			int? tmpCalledTimes = 0;

			tmpStage.ProgressChanged += (st, ea) =>
			{
				tmpCalledByStage = st;
				tmpCalledWithEventArgs = ea;
				tmpCalledTimes++;
			};
			tmpStage.CallNotifyProgressChange(tmpEA);

			Assert.AreEqual(1, tmpCalledTimes);
			Assert.AreEqual(tmpStage, tmpCalledByStage);
			Assert.AreEqual(tmpEA, tmpCalledWithEventArgs);
		}

		[TestMethod]
		public void HasFinalizerStageThrowsAndHasFinalizerStageIsFalseByDefault()
		{
			MockInstallationStage tmpStage = new MockInstallationStage(this.ProcessManager, this.FSContentProvider, this.ShutdownAction);

			Assert.IsFalse(tmpStage.HasFinalizerStage);
			AssertExtensions.Throws<NotSupportedException>(() => tmpStage.FinalizeStage());
		}

		[TestMethod]
		public void RollbackAndRollbackAsyncThrowsAndSupportsRollbackIsFalse()
		{
			MockInstallationStage tmpStage = new MockInstallationStage(this.ProcessManager, this.FSContentProvider, this.ShutdownAction);

			Assert.IsFalse(tmpStage.SupportsRollback);
			AssertExtensions.Throws<NotSupportedException>(() => tmpStage.RollBack());
			AssertExtensions.Throws<NotSupportedException>(() => tmpStage.RollBackAsync());
		}

		private IFileSystemContentProvider FSContentProvider
		{
			get
			{
				return this.mockFSContentProvider.Object;
			}
		}

		private IProcessManager ProcessManager
		{
			get
			{
				return this.mockProcessManager.Object;
			}
		}

		private Action ShutdownAction
		{
			get
			{
				return this.mockShutdownAction.Action;
			}
		}
	}
}
